using System;
using System.Collections.Generic;
using System.Linq;
using System.Diagnostics;
using System.Globalization;
using System.Threading;
using System.Windows.Forms;
using System.Drawing;
using System.Text;

using Routine.Utilities;

using Styx;
using Styx.CommonBot;
using Styx.CommonBot.Routines;
using Styx.Helpers;
using Styx.TreeSharp;
using Styx.WoWInternals;
using Styx.WoWInternals.WoWObjects;
using Styx.Common.Helpers;
using Styx.Pathing;

using CommonBehaviors.Actions;

using Singular.Dynamics;
using Singular.Helpers;
using Singular.Managers;
using Singular.Settings;

using Action = Styx.TreeSharp.Action;
//
using Rest = Singular.Helpers.Rest;
//
using yoyo;



namespace Surv
{
    public class Routine : CombatRoutine
    {
        /// <summary>
        /// The name of this CombatRoutine
        /// </summary>
        /// <value>
        /// The name.
        /// </value>
        public override string Name { get { return "Surv1.0"; } }


        /// <summary>
        /// The <see cref="T:Styx.WoWClass"/> to be used with this routine
        /// </summary>
        /// <value>
        /// The class.
        /// </value>
        public override WoWClass Class { get { return WoWClass.Hunter; } }

        public override bool WantButton { get { return true; } }

		private Composite _combat, _buffs;

        public override Composite CombatBehavior { get { return _combat; } }
        public override Composite PreCombatBuffBehavior { get { return _buffs; } }
        public override Composite CombatBuffBehavior { get { return _buffs; } }

         public override void OnButtonPress()
        {
            //Logger.Write(Colors.Yellow, "Config opened!");
            yoyo.yoyoForm1 myConfig = new yoyo.yoyoForm1();
            myConfig.ShowDialog();
        }

		public DateTime LastCastTime = DateTime.Now;

        public override void Initialize()
        {
            _combat = CreateCombat();
            _buffs = CreateBuffs();
        }

		private bool IsTargetBoss()
        {
            if (StyxWoW.Me.CurrentTarget.CreatureRank == WoWUnitClassificationType.WorldBoss ||
               (StyxWoW.Me.CurrentTarget.Level >= 90 && StyxWoW.Me.CurrentTarget.Elite && StyxWoW.Me.CurrentTarget.MaxHealth > 10000000))
                return true;

            else return false;
        }
		
		private int addCount()
        {
            int count = 0;
            foreach (WoWUnit u in ObjectManager.GetObjectsOfType<WoWUnit>(true, true))
            {
                if (u.IsAlive
                    && StyxWoW.Me.CurrentTarget != null
                    && u.Guid != StyxWoW.Me.Guid
                    && !u.IsFriendly
                    && (u.IsHostile || u.IsNeutral)
                    && !u.IsCritter
                    && u.Distance2DSqr <= 40*40
                    //&& u.Location.Distance(StyxWoW.Me.Location) <= 30
                    && (u.IsTargetingMyPartyMember || u.IsTargetingMyRaidMember || u.IsTargetingMeOrPet || u.IsTargetingAnyMinion))
                {
                    count++;
                }
            }
            return count;
        }

        private int RAOEaddCount()
        {
            int count = 0;
            foreach (WoWUnit u in ObjectManager.GetObjectsOfType<WoWUnit>(true, true))
            {
                if (u.IsAlive
                    && StyxWoW.Me.CurrentTarget != null
                    && u.Guid != StyxWoW.Me.Guid
                    && !u.IsFriendly
                    && (u.IsHostile || u.IsNeutral)
                    && !u.IsCritter
                    && u.Location.Distance(StyxWoW.Me.CurrentTarget.Location) <= 15//&& u.IsWithinMeleeRange
					//&& u.NearbyUnfriendlyUnits.Count(u => u.Distance <= 8) >= 2
                    && (u.IsTargetingMyPartyMember || u.IsTargetingMyRaidMember || u.IsTargetingMeOrPet || u.IsTargetingAnyMinion))
                {
                    count++;
                }
            }
            return count;
        }

        private Composite sCast(string spell, Selection<bool> reqs = null, Selection<WoWUnit> onTarget = null, bool ignoreCheck = false)
        {
            return
                new Decorator(
                    ret => (((reqs != null && reqs(ret)) || (reqs == null)) && SpellManager.CanCast(spell)) || ignoreCheck,
                    new Action(ret => SpellManager.Cast(spell, (onTarget != null ? onTarget(ret) : null))));
        }

        Composite CreateBuffs()
        {
            return new PrioritySelector(
				//Cast("Power Word: Fortitude", ret => !StyxWoW.Me.HasAura("Power Word: Fortitude") && !StyxWoW.Me.Mounted),
				//Cast("Inner Fire", ret => !StyxWoW.Me.HasAura("Inner Fire") && !StyxWoW.Me.Mounted),
				//Cast("Shadowform", ret => !StyxWoW.Me.HasAura("Shadowform")),
				new Decorator(ret =>
					!StyxWoW.Me.HasAura("Visions of Insanity") && !StyxWoW.Me.Mounted,
					new Action(abc =>
					{
						WoWItem item = StyxWoW.Me.BagItems.Find(ret => ret.Entry == 86569);
                            if (item != null) item.Use(); return RunStatus.Failure;
					}))
				
			);
			
        }

		
		
        Composite CreateCombat()
        {
			return new Decorator ( ctz => !StyxWoW.Me.Mounted || !StyxWoW.Me.HasAura("Drink") || !StyxWoW.Me.HasAura("Food") || !StyxWoW.Me.HasAura("First Aid"),
				new PrioritySelector(

				//Movement
				new Decorator ( ctt =>!StyxWoW.Me.Mounted && !yoyoSettings.myPrefs.DisableAllMovement && StyxWoW.Me.CurrentTarget.Distance > 30,				
				new Action(ret => 
				{ 
                        Navigator.MoveTo(attackPoint);
				})),
				new Action(ret => 
				{ 
                    if (!StyxWoW.Me.Mounted && !yoyoSettings.myPrefs.DisableAllMovement && StyxWoW.Me.CurrentTarget.Distance <= 30 && StyxWoW.Me.IsMoving)
                    {
                        WoWMovement.MoveStop();
						//return;
                    };
					return RunStatus.Failure;
				}),
				//Facing
				new Action(ret => 
				{ 
					if (!StyxWoW.Me.Mounted && !yoyoSettings.myPrefs.DisableAllFacement)
					{
						StyxWoW.Me.CurrentTarget.Face();
					};
					return RunStatus.Failure; 
				}),


                // Interrupt please.
                Cast("Silencing Shot", ret => StyxWoW.Me.CurrentTarget.IsCasting && StyxWoW.Me.CurrentTarget.CanInterruptCurrentSpellCast),
                //Cast("Pyschic Horror", ret => StyxWoW.Me.CurrentTarget.IsCasting && StyxWoW.Me.CurrentTarget.CanInterruptCurrentSpellCast),
                
				// Defensive CD
				Cast("Deterence", ret => StyxWoW.Me.HealthPercent < 15),
				//Spell.CastOnGround("Psyfiend", ctx => StyxWoW.Me.CurrentTarget.Location, ret =>	StyxWoW.Me.HealthPercent < 50 && Unit.NearbyUnfriendlyUnits.Count(u => u.Distance <= 8) >= 2),
/*
                Spell.Cast("Misdirection", ctx => StyxWoW.Me.Pet, ret => StyxWoW.Me.GotAlivePet 
                                    && StyxWoW.Me.Combat
                                    && !StyxWoW.Me.HasAura("Misdirection")
                                    && !Group.Tanks.Any(t => t.IsAlive && t.Distance < 100)				
									&& StyxWoW.Me.CurrentTarget.ThreatInfo.RawPercent > 75),
*/								
				new Action(ret => 
				{ 
					if (SpellManager.CanCast("Misdirection") && !SpellManager.Spells["Misdirection"].Cooldown)
					{
						if (StyxWoW.Me.FocusedUnit != null)
						{
							Lua.DoString("RunMacroText('/cast [@focus,exists] Misdirection');");
						} 
						else if (Group.Tanks.Any(t => t.IsAlive && t.Distance < 100))
						{
							Spell.Cast("Misdirection", ctx => Group.Tanks.Any(t => t.IsAlive && t.Distance < 100));
						}
						else if ( StyxWoW.Me.GotAlivePet && StyxWoW.Me.CurrentTarget.ThreatInfo.RawPercent > 75)
						{
							Lua.DoString("RunMacroText('/cast [@pet,exists] Misdirection');");
						}
					}
					return RunStatus.Failure;
				}),
				
                new Action(ret => { UseTrinkets(); return RunStatus.Failure; }),
                Spell.Cast("Berserker Rage"),
				
				// AOE
				new Decorator(ret => addCount() > 1,
                new PrioritySelector(
				Cast("Multi-Shot", ret => RAOEaddCount() > 2 && StyxWoW.Me.ActiveAuras.Any(u => u.Key == "Thrill of the Hunt")),
				Cast("Multi-Shot", ret => RAOEaddCount() > 2),
				Spell.CastOnGround("Explosive Trap", ctx => StyxWoW.Me.CurrentTarget.Location, ret => RAOEaddCount() > 2 && StyxWoW.Me.HasAura("Trap Launcher")), // && Unit.NearbyUnfriendlyUnits.Count(u => u.Distance <= 8) >= 2),
				Cast("Multi-Shot", ret => RAOEaddCount() > 2),
				Spell.Cast("Kill Shot"),
				Spell.Cast("Explosive Shot", ret=> !SpellManager.GlobalCooldown && StyxWoW.Me.ActiveAuras.Any(u => u.Key == "Lock and Load")),
				Spell.Cast("Arcane Shot", ret=> StyxWoW.Me.ActiveAuras.Any(u => u.Key == "Thrill of the Hunt")),
				Spell.Cast("Black Arrow"),
				Spell.Cast("Dire Beast"),
				Spell.Cast("Glaive Toss"),
				Spell.Cast("Explosive Shot"), //ret=> TargetDebuffDuration("Explosive Shot") <= 2),
				sCast("Serpent Sting", rt=> (DoTAddTarget("Serpent Sting") != null) && addCount() <= 2, rt=>DoTAddTarget("Serpent Sting")),
				Spell.Cast("Arcane Shot"), //ret => StyxWoW.Me.CurrentFocus > 30),
				Spell.Cast("Cobra Shot")				
				)),
				//Normal

				Spell.Cast("Kill Shot"),
				Spell.Cast("Explosive Shot", ret=> StyxWoW.Me.ActiveAuras.Any(u => u.Key == "Lock and Load")),
				Spell.Cast("Arcane Shot", ret=> StyxWoW.Me.ActiveAuras.Any(u => u.Key == "Thrill of the Hunt")),
				Spell.Cast("Black Arrow"),
				Spell.Cast("Explosive Shot"),// ret=> TargetDebuffDuration("Explosive Shot") <= 2),
				Spell.Cast("Serpent Sting",rt=> TargetDebuffDuration("Serpent Sting") <= 3),
				Spell.Cast("Glaive Toss"),
				Spell.Cast("Dire Beast"),
				Spell.Cast("A Murder of Crows"),
				Spell.Cast("Arcane Shot"),// ret => StyxWoW.Me.CurrentFocus > 40),
				Spell.Cast("Cobra Shot")//, ret => StyxWoW.Me.CurrentFocus < 60)				
                ));
        }

        private static WoWUnit GetTarget()
        {

            foreach (WoWUnit unit in ObjectManager.GetObjectsOfType<WoWUnit>(false,false))
            {
                if (unit.Distance2DSqr > 40 * 40) continue;
                if (unit.HealthPercent < 30 && !unit.Elite) continue;
                if (!unit.InLineOfSpellSight) continue;
                //if (!Extensions.ValidUnit(unit)) continue;
                if (!unit.IsTargetingMeOrPet && !unit.IsTargetingMyPartyMember && !unit.IsTargetingMyRaidMember && !unit.Aggro && !unit.PetAggro) continue;
                if (unit.Attackable || !unit.IsFriendly) return unit;
            }

            return null;
        }

        private static WoWUnit DoTAddTarget(string spellName)
        {

            foreach (WoWUnit unit in ObjectManager.GetObjectsOfType<WoWUnit>(false,false))
            {
                if (unit.Distance2DSqr > 40 * 40) continue;
                if (unit.HealthPercent < 30 && !unit.Elite) continue;
                if (!unit.InLineOfSpellSight) continue;
                //if (!Extensions.ValidUnit(unit)) continue;
                if (!unit.IsTargetingMeOrPet && !unit.IsTargetingMyPartyMember && !unit.IsTargetingMyRaidMember && !unit.Aggro && !unit.PetAggro) continue;
                //if (unit.ActiveAuras[spellName].TimeLeft.TotalSeconds > 3) continue;
                

                if (!unit.HasMyAura(spellName)) return unit;
            }

            return null;
        }

        private WoWUnit GetTargetMissingDebuff(WoWUnit near, string debuff, float distance = -1f)
        {
            return UnfriendlyUnits.FirstOrDefault(u => u.Location.Distance(near.Location) <= distance && !u.HasMyAura(debuff));
        }

        private IEnumerable<WoWUnit> UnfriendlyUnits
        {
            get
            {
                return
                    ObjectManager.GetObjectsOfType<WoWUnit>(true, false).Where(u => !u.IsDead && u.CanSelect && u.Attackable && !u.IsFriendly);
            }
        }

        void UseTrinkets()
        {
            var firstTrinket = StyxWoW.Me.Inventory.Equipped.Trinket1;
            var secondTrinket = StyxWoW.Me.Inventory.Equipped.Trinket2;


            if(firstTrinket != null && CanUseEquippedItem(firstTrinket))
                firstTrinket.Use();


            if(secondTrinket != null && CanUseEquippedItem(secondTrinket))
                secondTrinket.Use();


        }
        private static bool CanUseEquippedItem(WoWItem item)
        {
            // Check for engineering tinkers!
            string itemSpell = Lua.GetReturnVal<string>("return GetItemSpell(" + item.Entry + ")", 0);
            if (string.IsNullOrEmpty(itemSpell))
                return false;


            return item.Usable && item.Cooldown <= 0;
        }

        WoWPoint attackPoint
        {
            get
            {
                if (StyxWoW.Me.GotTarget)
                {
                    //return WoWMovement.CalculatePointFrom(StyxWoW.Me.CurrentTarget.Location, (float)SpellManager.Spells["Death Strike"].MaxRange + 1);
                    return WoWMovement.CalculatePointFrom(StyxWoW.Me.CurrentTarget.Location, (float)SpellManager.Spells["Arcane Shot"].MaxRange - 3);
                }
                else
                {
                    WoWPoint noSpot = new WoWPoint();
                    return noSpot;
                }
            }
        }

        private delegate T Selection<out T>(object context);
        Composite Cast(string spell, Selection<bool> reqs = null)
        {
            return
                new Decorator(
                    ret => ((reqs != null && reqs(ret)) || (reqs == null)) && SpellManager.CanCast(spell),
                    new Action(ret => SpellManager.Cast(spell)));
        }

        public static double TargetDebuffDuration(String spellName)
        {
            return StyxWoW.Me.CurrentTarget.GetAuraTimeLeft(spellName, true).TotalSeconds;
        }

        public static TimeSpan GetSpellCooldown(string spell)
        {
            SpellFindResults results;
            if (SpellManager.FindSpell(spell, out results))
            {
                if (results.Override != null)
                    return results.Override.CooldownTimeLeft;
                return results.Original.CooldownTimeLeft;
            }


            return TimeSpan.MaxValue;
        }
		
    }
    public static class Extensions
    {
        public static bool HasAura(this WoWUnit unit, string aura, int stacks, bool isMyAura = false, int msLeft = 0)
        {
            var result = unit.GetAuraByName(aura);
            if (result == null)
                return false;


            if (isMyAura && result.CreatorGuid != StyxWoW.Me.Guid)
                return false;


            if (result.TimeLeft.TotalMilliseconds > msLeft)
                return result.StackCount >= stacks;


            return false;
        }
    }
	
}